/**
 * 
 */
package com.trungsi.profile;

import java.util.concurrent.ConcurrentLinkedQueue;

import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

/**
 * @author trand
 *
 */
public class SimpleTracer implements Tracer {

	private static ThreadLocal<Call> callStack = new ThreadLocal<Call>();
	
	private static ConcurrentLinkedQueue<Call> traceQueue = new ConcurrentLinkedQueue<Call>();
	
	private TracePrinter printer;
	
	public SimpleTracer(TracePrinter printer) {
		this.printer = printer;
		initPrinterThread();
	}
	
	private void initPrinterThread() {
		Thread t = new Thread() {
			@Override
			public void run() {
				while (!Thread.currentThread().isInterrupted()) {
					try {
						Call call = traceQueue.poll();
						if (call != null)
							//call.print();
							printer.print(call);
						else 
							Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		t.setDaemon(true);
		t.start();
	}
	
	/* (non-Javadoc)
	 * @see com.trungsi.profile.Tracer#traceBegin(org.aspectj.lang.ProceedingJoinPoint)
	 */
	public void traceBegin(ProceedingJoinPoint joinPoint) {
		traceBegin(joinPoint.getStaticPart());
	}

	public void traceBegin(StaticPart staticPart) {
		doTraceBegin(staticPart, getTime());
	}
	
	public void traceEnd(StaticPart staticPart) {
		long endTime = getTime();
		doTraceEnd(endTime);
	}
	
	private long getTime() {
		return System.currentTimeMillis();
	}

	private void doTraceBegin(StaticPart staticPart, long globalStart) {
		String traceName = getTraceName(staticPart);
		Call call = new Call(callStack.get(), traceName);
		callStack.set(call);
		
		call.setGlobalStart(globalStart);
		call.setStartTime(getTime());
	}

	/* (non-Javadoc)
	 * @see com.trungsi.profile.Tracer#traceEnd(org.aspectj.lang.ProceedingJoinPoint)
	 */
	public void traceEnd(ProceedingJoinPoint joinPoint) {
		traceEnd(joinPoint.getStaticPart());
	}

	private void doTraceEnd(long endTime) {
		Call call = callStack.get();
		call.setEndTime(endTime);
		
		callStack.set(call.getParent());
		
		if (call.getEndTime() - call.getGlobalStart() == 0) {
			if (call.getParent() != null)
				//call.getParent().setLastChild(call.getPrevious());
                call.remove();
		} else {
			if (callStack.get() == null) {
				printTrace(call);
			}
			call.setGlobalEnd(getTime());
		}
		
		
	}

	private void printTrace(Call call) {
		traceQueue.add(call);
	}

	private String getTraceName(StaticPart staticPart) {
		Signature signature = staticPart.getSignature();
		String traceName = signature.getDeclaringTypeName() + 
							"." + signature.getName();
		
		return traceName;
	}
}
